package display3D.controller3D
{
	import Tools.Geometry;
	
	import alternativa.engine3d.core.Camera3D;
	import alternativa.engine3d.core.EllipsoidCollider;
	import alternativa.engine3d.core.Object3DContainer;
	import alternativa.engine3d.materials.FillMaterial;
	import alternativa.engine3d.primitives.Box;
	
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Vector3D;
	import flash.ui.Keyboard;

	/**
	 * Classe qui permet de se deplacer dans la scene
	 * @author Arnaud
	 */
	public class WalkController extends EventDispatcher
	{
		private var _MOUSE_SENSITIVITY:Number = 0.5;
		private var _SPEED:int = 20;
		private var _camera:Camera3D;
		private var _stage:Stage;
		private var lookDown : Boolean;
		private var lookUp : Boolean;
		private var lookLeft : Boolean;
		private var lookRight : Boolean;
		private var _avancer :Boolean;
		private var _reculer :Boolean;
		private var _gauche :Boolean;
		private var _droite :Boolean;
		private var _lastMouseX:Number;
		private var _lastMouseY:Number;
		private var _mouseLook:Boolean = false;
		private var _checkCollision:Boolean =false;
		private var _collider:EllipsoidCollider;
		private var _radiusCollision:Number = 5;
		private var _sceneCollision:Object3DContainer;
		private var _hauteurCamera:int = 180;
		
		
		/**
		 * Constructeur
		 * @param camera Camera de la scene
		 * @param stage Stage de l'application
		 * @param scene Conteneur de la scene
		 */
		public function WalkController(camera:Camera3D,stage:Stage,scene:Object3DContainer=null)
		{
			this._camera = camera;
			this._stage = stage;
			this._camera.rotationX = -100*Math.PI/180;
			this._collider = new EllipsoidCollider (_radiusCollision,_radiusCollision,_radiusCollision);
			this._sceneCollision = scene;
			initiatListener();
		}
		
		/**
		 * Initialisation des listeners
		 */
		private function initiatListener():void
		{
			_stage.addEventListener(Event.ENTER_FRAME, handleEnterFrame);
			_stage.addEventListener(KeyboardEvent.KEY_DOWN, handleKeyDown);
			_stage.addEventListener(KeyboardEvent.KEY_UP, handleKeyUp);
			_stage.addEventListener(MouseEvent.MOUSE_DOWN,handleMouseDown);
			_stage.addEventListener(MouseEvent.MOUSE_UP,handleMouseUp);
		}
		
		/**
		 * Vérifie si il y a collision avec des objet 3D Alterntiva en fonction de la destionation de la souris
		 * @param displacement La destination de la camera
		 * @return true si collision false sinon
		 */
		private function checkCollisions(displacement:Vector3D):Boolean
		{
			if (_checkCollision && _sceneCollision!=null)
			{
				var cameraCoord:Vector3D = new Vector3D(_camera.x,_camera.y,_camera.z-(hauteurCamera-10));
				var collisionPoint:Vector3D = new Vector3D();
				var collisionPlane:Vector3D = new Vector3D();
				var onGround:Boolean = _collider.getCollision(cameraCoord,displacement, collisionPoint, collisionPlane, _sceneCollision);
				
				if (onGround) {
					if(this._avancer)
					{
						reculer(true);
						reculer(true);
						this._avancer =false;
					}
					if (this._reculer)
					{
						avancer(true);
						avancer(true);
						this._reculer=false;
					}
					
					if (this._gauche)
					{
						droite(true);
						droite(true);
						this._droite=false;
					}
					if (this._droite)
					{
						gauche(true);
						gauche(true);
						this._gauche=false;
					}
					
					return true;
				} else {
					return false;
				}
			}
		}
		
		/**
		 * Bouge la caméra vers l'avant et vérifie si il n'y a pas collision
		 * @param go true si le deplacement ne doit pas se faire 
		 */		
		private function avancer(go:Boolean=false):void
		{
			var deplacementX:* = Math.sin(-_camera.rotationZ) * _SPEED;
			var deplacementY:* = Math.cos(-_camera.rotationZ) * _SPEED;
			var deplacementZ:* = _hauteurCamera;
			var displacement:Vector3D = new Vector3D(deplacementX,deplacementY,deplacementZ);
			if (!go && !checkCollisions(displacement));
			{
				_camera.x += deplacementX;
				_camera.y += deplacementY;
				_camera.z = deplacementZ;	
			}
			
		}
		/**
		 * Bouge la caméra vers l'arrière et vérifie si il n'y a pas collision
		 * @param go true si le deplacement ne doit pas se faire 
		 */
		private function reculer(go:Boolean=false):void
		{
			var deplacementX:* = -Math.sin(-_camera.rotationZ) * _SPEED;
			var deplacementY:* = -Math.cos(-_camera.rotationZ) * _SPEED;
			var deplacementZ:* = _hauteurCamera;
			var displacement:Vector3D = new Vector3D(deplacementX,deplacementY,deplacementZ);
			if (!go && !checkCollisions(displacement));
			{
				_camera.x += deplacementX;
				_camera.y += deplacementY;
				_camera.z = deplacementZ;
			}
		}
		
		/**
		 * Bouge la caméra vers la gauche et vérifie si il n'y a pas collision
		 * @param go true si le deplacement ne doit pas se faire 
		 */
		private function gauche(go:Boolean=false):void
		{
			var deplacementX:* = -Math.cos(-_camera.rotationZ) * _SPEED;
			var deplacementY:* = Math.sin(-_camera.rotationZ) * _SPEED;
			var deplacementZ:* = _hauteurCamera;
			var displacement:Vector3D = new Vector3D(deplacementX,deplacementY,deplacementZ);
			if (!go && !checkCollisions(displacement));
			{
				_camera.x += deplacementX;
				_camera.y += deplacementY;
				_camera.z = deplacementZ;
			}
		}
		
		/**
		 * Bouge la caméra vers la droite et vérifie si il n'y a pas collision
		 * @param go true si le deplacement ne doit pas se faire 
		 */
		private function droite(go:Boolean=false):void
		{
			var deplacementX:* = Math.cos(-_camera.rotationZ) * _SPEED;
			var deplacementY:* = -Math.sin(-_camera.rotationZ) * _SPEED;
			var deplacementZ:* = _hauteurCamera;
			var displacement:Vector3D = new Vector3D(deplacementX,deplacementY,deplacementZ);
			if (!go && !checkCollisions(displacement));
			{
				_camera.x += deplacementX;
				_camera.y += deplacementY;
				_camera.z = deplacementZ;
			}
		}
		
		/**
		 * Dirige la caméra en fonction du mouvement de la souris
		 */
		private function mouseLook():void
		{
			var movementMouseX:Number = (_stage.mouseX - _lastMouseX);
			var movementMouseY:Number = (_stage.mouseY - _lastMouseY);
			_camera.rotationX -= movementMouseY * Math.PI / 180 * _MOUSE_SENSITIVITY;
			_camera.rotationZ -= movementMouseX * Math.PI / 180 * _MOUSE_SENSITIVITY;
			_lastMouseX = _stage.mouseX;
			_lastMouseY = _stage.mouseY;
		}
		
		
		/**
		 * Dirige la caméra en fonction des flèche directionnel du clavier
		 * @param event Event.ENTER_FRAME
		 */
		private function handleEnterFrame(event : Event) : void
		{
			if (lookDown)
			{
				_camera.rotationX -= 1 * Math.PI / 180 ;
			}
			if (lookUp)
			{
				_camera.rotationX += 1 * Math.PI / 180;
			}
			if (lookRight)
			{
				_camera.rotationZ -= 1 * Math.PI / 180;
			}
			if (lookLeft)
			{
				_camera.rotationZ += 1 * Math.PI / 180;
			}
			if (_avancer)
			{
				avancer();
			}
			if (_reculer)
			{
				reculer();
			}
			if (_gauche)
			{
				gauche();
			}
			if (_droite)
			{
				droite();
			}
			if (_mouseLook)
			{
				mouseLook();
			}
			
		}
		
		/**
		 * Gestionnaire des évènement clavier
		 * @param event KeyboardEvent.KEY_DOWN
		 */
		private function handleKeyDown(event : KeyboardEvent) : void
		{
			switch(event.keyCode)
			{
				case Keyboard.DOWN:
					lookDown = true;
					break;
				case Keyboard.UP:
					lookUp= true;
					break;
				case Keyboard.LEFT:
					lookLeft = true;
					break;
				case Keyboard.RIGHT:
					lookRight = true;
					break;
				case 90:
					_avancer = true;
					break;
				case 83:
					_reculer = true;
					break;
				case 81:
					_gauche = true;
					break;
				case 68:
					_droite = true;
					break;
				default:
			}
		}
		
		/**
		 * Gestionnaire des évènement clavier
		 * @param event KeyboardEvent.KEY_UP
		 */
		private function handleKeyUp(event : KeyboardEvent) : void
		{
			switch(event.keyCode)
			{
				case Keyboard.DOWN:
					lookDown = false;
					break;
				case Keyboard.UP:
					lookUp= false;
					break;
				case Keyboard.LEFT:
					lookLeft = false;
					break;
				case Keyboard.RIGHT:
					lookRight = false;
					break;
				case 90:
					_avancer = false;
					break;
				case 83:
					_reculer = false;
					break;
				case 81:
					_gauche = false;
					break;
				case 68:
					_droite = false;
					break;
				default:
			}
		}
		
		/**
		 * Déclenché lorsque le bouton gauche de la souris est enfoncé
		 * @param evt MouseEvent.MOUSE_DOWN
		 */
		private function handleMouseDown(evt:MouseEvent):void
		{
			_mouseLook = true;
			_lastMouseX = _stage.mouseX;
			_lastMouseY = _stage.mouseY;
			
		}
		
		/**
		 * Déclenché lorsque le bouton gauche de la souris est relaché
		 * @param evt MouseEvent.MOUSE_UP
		 */
		private function handleMouseUp(evt:MouseEvent):void
		{
			_mouseLook = false;
		}
		
		/**
		 * Place la camera à la position x et y demandé
		 * @param x Position x 
		 * @param y Position y
		 */
		public function startPosition(x:Number,y:Number):void
		{
			_camera.x = x;
			_camera.y = y;
			_camera.z = _hauteurCamera;
		}

		/**
		 * Vitesse de déplacement
		 * @default 20
		 */
		public function get SPEED():int
		{
			return _SPEED;
		}

		/**
		 * @private
		 */
		public function set SPEED(value:int):void
		{
			_SPEED = value;
		}

		/**
		 * Sensibilité de la souris
		 * @default 0.5
		 */
		public function get MOUSE_SENSITIVITY():Number
		{
			return _MOUSE_SENSITIVITY;
		}

		/**
		 * @private
		 */
		public function set MOUSE_SENSITIVITY(value:Number):void
		{
			_MOUSE_SENSITIVITY = value;
		}

		/**
		 * Represente la taille de la sphere autour de la camera
		 * </br>
		 * Plus la taille est grande plus la collision se fera de loin
		 * @default 5
		 */
		public function get radiusCollision():Number
		{
			return _radiusCollision;
		}

		/**
		 * @private
		 */
		public function set radiusCollision(value:Number):void
		{
			_radiusCollision = value;
		}

		/**
		 * True pour gérer la collision false sinon
		 * @default false
		 */
		public function get checkCollision():Boolean
		{
			return _checkCollision;
		}

		/**
		 * @private
		 */
		public function set checkCollision(value:Boolean):void
		{
			_checkCollision = value;
		}

		/**
		 * Hauteur de la caméra
		 * @default 180
		 */
		public function get hauteurCamera():int
		{
			return _hauteurCamera;
		}

		/**
		 * @private
		 */
		public function set hauteurCamera(value:int):void
		{
			_hauteurCamera = value;
		}


	}
}